function r = ensemble_test_slle(stegoMat, coverMat, modelFileName, ratio, TURN, SUB_TURN, verbose, KK, DD, a)
%ENSEMBLE_TEST_SLLE train & test ensemble models.
%  Before begining, it search for best D_SUB & L for ensemble learning.
%  Then this function train and test ensemble models several times in a
%  loop.
%  In each loop, it splits training and testing sets, then use training
%  set to train ensemble learner model. Use testing set to asset the
%  performance of ensemble learner.
%
% Usage:
%  ensemble_test_slle(stegoMat, coverMat[, modelFileName, ratio, TURN, SUB_TURN, verbose])
%  modelFileName: name of file to save the best model. If it is a number,
%                 for example, 0, model is not saved.
%  verbose      : 1 to display extra messages, 0 to disable it. 
%                 Default value is 0.
%  ratio        : nTrain/(nTrain+nTest). 
%                 Default value is 0.5, i.e., trn/tst = 1.
%  TURN         : turns of train / test in total. Default value is 20.
%  SUB_TURN     : turns for find best args. Default value is 5.

%% initialize variables
if (nargin < 3)
    modelFileName = 0;
end
if (nargin < 4)
    ratio = 0.5;
end
if (nargin < 5)
    TURN = 20;
end
if (nargin < 6)
    SUB_TURN = 5;
end
if (nargin < 7)
    verbose = 0;
end
if (nargin < 8)
    KK = 150;
end
if (nargin < 9)
    DD = 2000;
end
if (nargin < 10)
    a = 1;
end
r.ratio = ratio;
r.turn = TURN;
r.sub_turn = SUB_TURN;
r.verbose = verbose;
r.KK = KK;
r.DD = DD;
r.a = a;

%% manually seperate training set and testing set
[sM, sN] = size(stegoMat);
[cM, cN] = size(coverMat);
if (sN ~= cN)
    error('size of stegoMat %dx%d & coverMat %dx%d does not match',sM,sN,cM,cN);
end
M = floor(ratio * min(sM,cM));
tM = sM-M+cM-M;
fprintf('trn/tst = %d/%d\n',M*2,tM);

%% get D_SUB & L
if 1
    fprintf('Find best values for D_SUB and L.\n');
    ak = zeros(SUB_TURN, 1);
    al = zeros(SUB_TURN, 1);
    ae = zeros(SUB_TURN, 1);
    tic;
    for i=1:SUB_TURN
        if verbose
            fprintf('round %d\n',i);
        else
            fprintf('.');
        end
        sP = randperm(sM);
        cP = randperm(cM);
        train_stegoMat = stegoMat(sP(1:M),:);
        train_coverMat = coverMat(cP(1:M),:);
        
        old_train = [train_stegoMat;train_coverMat];
        ltrain = [ones(M,1);-1*ones(M,1)];
        train = slle(old_train', ltrain, KK, DD, a)';
        train_stegoMat = train(1:M, :);
        train_coverMat = train(M+1:2*M, :);
        clear train;

        %% call ensemble
        r1 = ensemble_wrap(train_stegoMat, train_coverMat, 'automatic', 'automatic', 0.5, 'auto', verbose);
        ae(i) = r1.testing_error;
        ak(i) = r1.optimal.k;
        al(i) = r1.optimal.L;
    end
    T = toc;
    d_sub = round(sum(ak) / SUB_TURN);
    L = round(sum(al) / SUB_TURN);
    r.d_sub = d_sub;
    r.L = L;
    r.KL_TIME = T;
    r.KL_ERR = sum(ae) / SUB_TURN;
end
%% train and test
fprintf('Train & test:\n');
disp(r)
errors = nan(TURN, 1);
models = cell(TURN, 1);
tic;
for i=1:TURN
    if verbose
        fprintf('round %d\n',i);
    else
        fprintf('.');
    end
    sP = randperm(sM);
    cP = randperm(cM);
    tP = randperm(tM);
    train_stegoMat = stegoMat(sP(1:M),:);
    test_stegoMat = stegoMat(sP(M+1:end),:);
    train_coverMat = coverMat(cP(1:M),:);
    test_coverMat = coverMat(cP(M+1:end),:);
    testMat = [test_stegoMat; test_coverMat];
    testMat = testMat(tP,:);
    resultMat = [-1*ones(sM-M,1);+1*ones(cM-M,1)];
    resultMat = resultMat(tP,:);
    
    old_train = [train_stegoMat;train_coverMat];
    ltrain = [ones(M,1);-1*ones(M,1)];
    train = slle(old_train', ltrain, KK, DD, a)';

    train_stegoMat = train(1:M, :);
    train_coverMat = train(M+1:2*M, :);
    testMat = deTestD(old_train',testMat',train', KK)';
    clear old_train train

    %% call ensemble_train
    r1 = ensemble_train_wrap(train_stegoMat, train_coverMat, d_sub, L, 'auto', verbose);
    
    %% test
    returnMat = ensemble_predict(testMat, r1);
    p = nnz(returnMat - resultMat) / tM;
    errors(i) = p;
    models{i} = r1;
    if verbose
        fprintf('accuracy = %0.2f%%\n',(1-p)*100);
    end
end
T=toc;
r.TIME = T;
r.avg_err= sum(errors) / TURN;
r.min_err = min(errors);
ind = find(errors == r.min_err);
ind = ind(1); % only choose the first one
model = models{ind};
r.best_model = model;

%% save model
if ischar(modelFileName)
    fprintf('saving model to %s.\n', modelFileName);
    save(modelFileName,'model');
end

end
